using System.Collections;
using System.Data.SqlTypes;
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Server;


namespace RegexTools
{
    public sealed class RegexFn
    {
        [SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true, Name = "fn_regex_ismatch", SystemDataAccess = SystemDataAccessKind.None)]
        public static SqlBoolean RegExIsMatch(SqlString input, SqlString pattern, SqlBoolean ignore_case)
        {
            //If either input is NULL, return NULL
            if (input.IsNull || pattern.IsNull)
            {
                return SqlBoolean.Null;
            }

            //Execute the regular expression and return the result
            return new SqlBoolean(Regex.IsMatch(input.Value, pattern.Value, ignore_case.Value ? RegexOptions.IgnoreCase : RegexOptions.None));
        }

        [SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true, Name = "fn_regex_replace", SystemDataAccess = SystemDataAccessKind.None)]
        public static SqlString RegExReplace(SqlString input, SqlString pattern, SqlString replacement, SqlBoolean ignore_case)
        {
            //If either input is NULL, return NULL
            if (input.IsNull || pattern.IsNull)
            {
                return SqlString.Null;
            }

            //Execute the regular expression and return the result
            return new SqlString(Regex.Replace(input.Value, pattern.Value, replacement.Value, ignore_case.Value ? RegexOptions.IgnoreCase : RegexOptions.None));
        }

        [SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true, Name = "fn_regex_matches", SystemDataAccess = SystemDataAccessKind.None, FillRowMethodName = "GetRegExMatches")]
        public static IEnumerable RegExMatches(SqlString input, SqlString pattern, SqlBoolean ignore_case)
        {
            //If either input is NULL, return NULL
            if (input.IsNull || pattern.IsNull)
            {
                return null;
            }

            //Execute the regular expression and return the result
            return Regex.Matches(input.Value, pattern.Value, ignore_case.Value ? RegexOptions.IgnoreCase : RegexOptions.None);
        }
        private static void GetRegExMatches(object input, out SqlString match, out SqlInt32 matchIndex, out SqlInt32 matchLength)
        {
            //Get a handle to the match object
            Match RegExMatch = (Match)input;

            //Set the column values
            match = new SqlString(RegExMatch.Value);
            matchIndex = new SqlInt32(RegExMatch.Index);
            matchLength = new SqlInt32(RegExMatch.Length);
        }

        [SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true, Name = "fn_regex_split", SystemDataAccess = SystemDataAccessKind.None, FillRowMethodName = "GetRegExSplits")]
        public static IEnumerable RegExSplit(SqlString input, SqlString pattern, SqlBoolean ignore_case)
        {
            //If either input is NULL, return NULL
            if (input.IsNull || pattern.IsNull)
            {
                return null;
            }

            //Execute the regular expression and return the result
            return Regex.Split(input.Value, pattern.Value, ignore_case.Value ? RegexOptions.IgnoreCase : RegexOptions.None);
        }
        private static void GetRegExSplits(object input, out SqlString match)
        {
            //Set the match
            match = new SqlString((string)input);
        }
 //clr.fn_regex_occurs - Custom regex function, advanced occurs function using regular expressions to return the number of occurences
        [SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true, Name = "fn_regex_occurs", SystemDataAccess = SystemDataAccessKind.None)]
        public static SqlInt64 RegExOccurs(SqlString input, SqlString pattern, SqlBoolean ignore_case)
        {
            //If inputs are null return null
            if (input.IsNull || pattern.IsNull)
            {
                return 0;
            }

            //Execute regex and return the result
            return (SqlInt64)(Regex.Matches(input.Value, pattern.Value, ignore_case.Value ? RegexOptions.IgnoreCase : RegexOptions.None).Count);  
         }


//clr.fn_regex_instr - Custom regex function, advanced instr function using regular expressions to return the position of occurence
        [SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true, Name = "fn_regex_instr", SystemDataAccess = SystemDataAccessKind.None)]
        public static SqlInt64 RegExInstr(SqlString input, SqlString pattern, SqlBoolean ignore_case)
        {            
            //If either input is NULL, return NULL
            if (input.IsNull || pattern.IsNull)
            {
                return 0 ;
            }

            //Rerurn the index of the pattern match
            return (SqlInt64)(Regex.Match(input.Value, pattern.Value, ignore_case.Value ? RegexOptions.IgnoreCase : RegexOptions.None).Index);
        }


//clr.fn_regex_substr - Custom regex function, advanced substr function using regular expressions to return the substring matching the pattern 
        [SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true, Name = "fn_regex_substr", SystemDataAccess = SystemDataAccessKind.None)]
        public static SqlString RegExSubstr(SqlString input, SqlString pattern, SqlBoolean ignore_case, SqlInt64 start, SqlInt64 occurence)
        {
            //If either input is NULL, return NULL
            if (input.IsNull || pattern.IsNull)
            {
                return SqlString.Null;
            }

            try
            {
               //Execute the regular expression and return the result
                return (Regex.Matches(input.ToString().Substring((int)start.Value), pattern.Value, ignore_case.Value ? RegexOptions.IgnoreCase : RegexOptions.None)[(int)occurence.Value].Value);
            }
            catch
            {
                return SqlString.Null;
            }

        }

        //TODO
        //regex_like

        private RegexFn()
        {
            //Since all of the methods in this class are static, there is no need for a public constructor
        }
    }
}

